# -*- rnc -*-
# RELAX NG Compact Syntax Grammar for the
# Atom Format Specification Version 11

namespace atom = "http://www.w3.org/2005/Atom"
namespace local = ""
namespace s = "http://www.ascc.net/xml/schematron"
namespace xhtml = "http://www.w3.org/1999/xhtml"

start = atomFeed | atomEntry
# Common attributes
atomCommonAttributes =
  attribute xml:base { atomUri }?,
  attribute xml:lang { atomLanguageTag }?,
  undefinedAttribute*
# Text Constructs
atomPlainTextConstruct =
  atomCommonAttributes,
  attribute type { "text" | "html" }?,
  text
atomXHTMLTextConstruct =
  atomCommonAttributes,
  attribute type { "xhtml" },
  xhtmlDiv
atomTextConstruct = atomPlainTextConstruct | atomXHTMLTextConstruct
# Person Construct
atomPersonConstruct =
  atomCommonAttributes,
  (element atom:name { text }
   & element atom:uri { atomUri }?
   & element atom:email { atomEmailAddress }?
   & extensionElement*)
# Date Construct
atomDateConstruct = atomCommonAttributes, xsd:dateTime
# atom:feed
atomFeed =
  [
    s:rule [
      context = "atom:feed"
      "\x{a}" ~
      "        "
      s:assert [
        test = "atom:author or not(atom:entry[not(atom:author)])"
        "An atom:feed must have an atom:author unless all of its atom:entry children have an atom:author."
      ]
      "\x{a}" ~
      "      "
    ]
  ]
  element atom:feed {
    atomCommonAttributes,
    (atomAuthor*
     & atomCategory*
     & atomContributor*
     & atomGenerator?
     & atomIcon?
     & atomId
     & atomLink*
     & atomLogo?
     & atomRights?
     & atomSubtitle?
     & atomTitle
     & atomUpdated
     & extensionElement*),
    atomEntry*
  }
# atom:entry
atomEntry =
  [
    s:rule [
      context = "atom:entry"
      "\x{a}" ~
      "        "
      s:assert [
        test =
          "atom:link[@rel='alternate'] or atom:link[not(@rel)] or atom:content"
        "An atom:entry must have at least one atom:link element with a rel attribute of 'alternate' or an atom:content."
      ]
      "\x{a}" ~
      "      "
    ]
    s:rule [
      context = "atom:entry"
      "\x{a}" ~
      "        "
      s:assert [
        test =
          "atom:author or ../atom:author or atom:source/atom:author"
        "An atom:entry must have an atom:author if its feed does not."
      ]
      "\x{a}" ~
      "      "
    ]
  ]
  element atom:entry {
    atomCommonAttributes,
    (atomAuthor*
     & atomCategory*
     & atomContent?
     & atomContributor*
     & atomId
     & atomLink*
     & atomPublished?
     & atomRights?
     & atomSource?
     & atomSummary?
     & atomTitle
     & atomUpdated
     & extensionElement*)
  }
# atom:content
atomInlineTextContent =
  element atom:content {
    atomCommonAttributes,
    attribute type { "text" | "html" }?,
    text*
  }
atomInlineXHTMLContent =
  element atom:content {
    atomCommonAttributes,
    attribute type { "xhtml" },
    xhtmlDiv
  }
atomInlineOtherContent =
  element atom:content {
    atomCommonAttributes,
    attribute type { atomMediaType }?,
    (text | anyElement)*
  }
atomOutOfLineContent =
  element atom:content {
    atomCommonAttributes,
    attribute type { atomMediaType }?,
    attribute src { atomUri },
    empty
  }
atomContent =
  atomInlineTextContent
  | atomInlineXHTMLContent
  | atomInlineOtherContent
  | atomOutOfLineContent
# atom:author
atomAuthor = element atom:author { atomPersonConstruct }
# atom:category
atomCategory =
  element atom:category {
    atomCommonAttributes,
    attribute term { text },
    attribute scheme { atomUri }?,
    attribute label { text }?,
    undefinedContent
  }
# atom:contributor
atomContributor = element atom:contributor { atomPersonConstruct }
# atom:generator
atomGenerator =
  element atom:generator {
    atomCommonAttributes,
    attribute uri { atomUri }?,
    attribute version { text }?,
    text
  }
# atom:icon
atomIcon = element atom:icon { atomCommonAttributes, atomUri }
# atom:id
atomId = element atom:id { atomCommonAttributes, atomUri }
# atom:logo
atomLogo = element atom:logo { atomCommonAttributes, atomUri }
# atom:link
atomLink =
  element atom:link {
    atomCommonAttributes,
    attribute href { atomUri },
    attribute rel { atomNCName | atomUri }?,
    attribute type { atomMediaType }?,
    attribute hreflang { atomLanguageTag }?,
    attribute title { text }?,
    attribute length { text }?,
    undefinedContent
  }
# atom:published
atomPublished = element atom:published { atomDateConstruct }
# atom:rights
atomRights = element atom:rights { atomTextConstruct }
# atom:source
atomSource =
  element atom:source {
    atomCommonAttributes,
    (atomAuthor*
     & atomCategory*
     & atomContributor*
     & atomGenerator?
     & atomIcon?
     & atomId?
     & atomLink*
     & atomLogo?
     & atomRights?
     & atomSubtitle?
     & atomTitle?
     & atomUpdated?
     & extensionElement*)
  }
# atom:subtitle
atomSubtitle = element atom:subtitle { atomTextConstruct }
# atom:summary
atomSummary = element atom:summary { atomTextConstruct }
# atom:title
atomTitle = element atom:title { atomTextConstruct }
# atom:updated
atomUpdated = element atom:updated { atomDateConstruct }
# Low-level simple types
atomNCName = xsd:string { minLength = "1" pattern = "[^:]*" }
# Whatever a media type is, it contains at least one slash
atomMediaType = xsd:string { pattern = ".+/.+" }
# As defined in RFC 3066
atomLanguageTag =
  xsd:string { pattern = "[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*" }
# Unconstrained; it's not entirely clear how IRI fit into
# xsd:anyURI so let's not try to constrain it here
atomUri = text
# Whatever an email address is, it contains at least one @
atomEmailAddress = xsd:string { pattern = ".+@.+" }
# Simple Extension
simpleExtensionElement = element * - atom:* { text }
# Structured Extension
structuredExtensionElement =
  element * - atom:* {
    (attribute * { text }+,
     (text | anyElement)*)
    | (attribute * { text }*,
       (text?, anyElement+, (text | anyElement)*))
  }
# Other Extensibility
extensionElement = simpleExtensionElement | structuredExtensionElement
undefinedAttribute =
  attribute * - (xml:base | xml:lang | local:*) { text }
undefinedContent = (text | anyForeignElement)*
anyElement =
  element * {
    (attribute * { text }
     | text
     | anyElement)*
  }
anyForeignElement =
  element * - atom:* {
    (attribute * { text }
     | text
     | anyElement)*
  }
# XHTML
anyXHTML =
  element xhtml:* {
    (attribute * { text }
     | text
     | anyXHTML)*
  }
xhtmlDiv =
  element xhtml:div {
    (attribute * { text }
     | text
     | anyXHTML)*
  }
